import initTimer from './timer'
import {nanoid} from 'nanoid'

class Danmu {
    constructor(player) {
        this.player = player
        this.timer = initTimer()
        this.frameTimer = null
        this.chats = []
        this.index = 0
        this.ways = {
            right: 0,
            top: 0,
            bottom: 0,
        }
        this.cacheWays = {
            right: [],
            top: [],
            bottom: [],
        }
        this.__initDOM()
        this.__initEvent()
        this.load(this.player.property.danmu)
    }

    __initDOM() {
        this.dom = document.createElement('div')
        this.dom.className = 'z-player-danmu'
        this.player.template.top.appendChild(this.dom)
        this.mask = document.createElement('div')
        this.mask.className = 'z-player-danmu_mask'
        this.player.template.top.appendChild(this.mask)
        this.context = document.createElement('canvas').getContext('2d')
        this.context.font = getComputedStyle(this.dom, null).getPropertyValue('font')
    }

    __initEvent() {
        Danmu.prototype.__handleResize = () => {
            this.player.__event.trigger('resize')
        }
        window.addEventListener('resize', this.__handleResize)
        this.player.__event.on('seeked', () => {
            this.seek()
        })
        this.player.__event.on('resize', () => {
            const items = this.dom.getElementsByClassName('z-player-danmu-item_right')
            for (let i = 0; i < items.length; i++) {
                let w = this.context.measureText(items[i].innerText).width + 10
                items[i].style.animationDuration = (this.player.container.offsetWidth + w) / 100 + 's'
                items[i].style.transform = `translateX(calc(-${this.player.container.offsetWidth + w}px))`
            }
        })
        this.player.__event.on('destroy', () => {
            window.removeEventListener('resize', this.__handleResize)
            clearTimeout(this.frameTimer)
        })

        let maskTimer = null
        if (!this.player.__util.isMobile) {
            this.player.template.top.addEventListener(this.player.__util.event.move, () => {
                if (maskTimer) clearTimeout(maskTimer)
                this.mask.style.display = 'none'
                maskTimer = setTimeout(() => this.mask.style.removeProperty('display'), 2000)
            })
        }
    }

    load(danmu) {
        this.visible = danmu.visible ? true : this.visible ? this.visible : true
        this.massive = danmu.massive ? true : this.massive ? !!this.massive : false
        this.playbackRate = !isNaN(danmu.playbackRate * 1) ?
            danmu.playbackRate * 1 : this.playbackRate ? this.playbackRate : 1
        this.screen = !isNaN(danmu.screen * 1) ? danmu.screen * 1 : this.screen ? this.screen : 1
        this.scale = !isNaN(danmu.scale * 1) ? danmu.scale * 1 : this.scale ? this.scale : 1
        this.opacity = !isNaN(danmu.opacity * 1) ? danmu.opacity * 1 : this.opacity ? this.opacity : 1
        if (typeof danmu.data === 'string') {
            let arr = []
            let parser = new DOMParser()
            let xmlDoc = parser.parseFromString(danmu.data + '','text/xml')
            let counts = xmlDoc.getElementsByTagName('d')
            for (let i = 0; i < counts.length; i++) {
                let p = counts[i].getAttribute('p').split(',')
                let type = 0
                if (p[1] === '4') {
                    type = 2
                }
                else if (p[1] === '5') {
                    type = 1
                }
                arr.push([parseFloat(p[0]), type, parseInt(p[3]), p[6], counts[i].textContent])
            }
            danmu.data = arr
        }
        if (!danmu.data || !(danmu.data instanceof Array) || danmu.length < 0) {
            this.player.notice(this.player.__translate('弹幕载入失败'))
            return
        }
        this.chats = danmu.data.sort((a, b) => a[0] - b[0])
        this.player.notice(this.player.__translate('弹幕载入成功'))
        this.seek()
        this.frameTimer = setTimeout(() => this.__frame(), 100)
        this.player.__event.trigger('danmuLoad')
    }

    seek(flag) {
        if (!flag) {
            this.index = 0
            this.ways = {
                right: 0,
                top: 0,
                bottom: 0,
            }
            this.cacheWays = {
                right: [],
                top: [],
                bottom: [],
            }
            this.timer.clean()
            this.dom.innerHTML = ''
        }
        for (let i = 0; i < this.chats.length; i++) {
            if (this.chats[i][0] >= this.player.video.currentTime) {
                this.index = i
                break
            }
            this.index = this.chats.length
        }
        this.player.__event.trigger('danmuSeek')
    }

    send(data) {
        if (!data || isNaN(data.type * 1) || isNaN(data.color * 1) || !data.text) {
            this.player.notice(this.player.__translate('数据格式错误'))
            return
        }
        let f = [isNaN(data.time * 1) ? null : data.time * 1, data.type + '', data['userId'] + '', data.color * 1, data.text + '']
        this.timer.setTimeout(() => this.__draw([f], true), !!f[0] ? 100 : f[0] + 100 - this.player.video.currentTime, nanoid())
    }

    __frame() {
        if (isNaN(this.opacity) || this.opacity < 0 || this.opacity > 1) this.opacity = 1
        this.visible = !!this.visible
        this.massive = !!this.massive
        if (this.player.container?.innerHTML === '' ?? true) return
        if (!this.visible) {
            this.dom.style.display = 'none'
            this.dom.innerHTML = ''
        } else {
            this.dom.style.opacity = this.opacity + ''
            this.dom.style.removeProperty('display')
            this.dom.childNodes.forEach(el => {
                let t = 'running'
                if (this.player.video.paused) t = 'paused'
                el.style['animation-play-state'] = t
            })
        }
        if (!this.player.video.paused) {
            let item = this.chats[this.index]
            let dans = []
            while (item && this.player.video.currentTime > parseFloat(item[0])) {
                dans.push(item)
                item = this.chats[++this.index]
            }
            this.__draw(dans, false)
        }
        if (this.player.video.paused) this.timer.pauseAll()
        else this.timer.playAll()
        // window.requestAnimationFrame(() => this.frame())
        this.frameTimer = setTimeout(() => this.__frame(), 100)
    }

    __draw(dans, flag) {
        if (isNaN(this.playbackRate) || this.playbackRate <= 0) this.playbackRate = 1
        if (isNaN(this.screen) || this.screen <= 0 || this.screen > 1) this.screen = 1
        if (isNaN(this.scale) || this.scale <= 0) this.scale = 1
        let size = (16 * this.scale < 12 && this.player.__util.isChrome ? 12 : 16 * this.scale) + 4 + this.scale
        let h = (this.player.container.offsetHeight * this.screen) - size
        let b = this.player.container.offsetHeight * (1 - this.screen)
        const docFragment = document.createDocumentFragment()
        const getWay = (el, type, obj) => {
            if (type === 'bottom' && this.screen < 0.75) return
            let position = -1
            el.classList.add(`z-player-danmu-item_${type}`)
            this.cacheWays[type] = [...new Set(this.cacheWays[type])]
            this.cacheWays[type].sort((a, b) => a - b)
            if (this.cacheWays[type].length > 0) {
                let arr = []
                position = this.cacheWays[type].shift()
                while ((type === 'bottom' ? position * size + b : position * size) >= h) {
                    arr.push(position)
                    if (this.cacheWays[type].length <= 0) {
                        position = -1
                        break
                    }
                    position = this.cacheWays[type].shift()
                }
                this.cacheWays[type] = [...this.cacheWays[type], ...arr]
            }
            if (position < 0) {
                position = this.ways[type]
                this.ways[type]++
                if (this.ways[type] > h / size && !this.massive) {
                    if (!flag) {
                        this.ways[type]--
                        return
                    }
                }
            }
            let __h = position % Math.ceil(h / size) * size
            el.style[type === 'bottom' ? 'bottom' : 'top'] = (type === 'bottom' ?  __h + b : __h) + 'px'
            if (type !== 'right') {
                if (position <= h / size) {
                    this.timer.setTimeout(() => this.cacheWays[type].push(position), 5000 * this.playbackRate, nanoid())
                }
                el.style.animationDuration = 5 * this.playbackRate + 's'
            } else {
                let w = this.context.measureText(obj[4]).width * this.scale
                if (position <= h / size) {
                    this.timer.setTimeout(() => this.cacheWays.right.push(position), (w + size) * 10 / this.playbackRate, nanoid())
                }
                el.style.animationDuration = (this.player.container.offsetWidth + w) / 100 / this.playbackRate + 's'
                el.style.transform = `translateX(calc(-${this.player.container.offsetWidth}px))`
            }
            el.innerHTML = obj[4]
            el.style.color = '#' + ('00000' + obj[3].toString(16)).slice(-6)
            el.addEventListener('animationend', () => {
                this.dom.removeChild(el)
            })
            el.classList.add('z-player-danmu-item_move')
            docFragment.appendChild(el)
        }
        for (let i = 0; i < dans.length; i++) {
            if (!isNaN(dans[i][3]) && dans[i][3] > 16777215) dans[i][3] = (16777215).toString(16)
            const item = document.createElement('div')
            item.classList.add('z-player-danmu-item')
            item.style.fontSize = this.scale + 'em'
            item.style.height = size + 'px'
            item.style.paddingBottom = this.scale + 'px'
            if (flag) item.style.border = 'solid 1px #fff'
            switch (dans[i][1] + '') {
                case '1': getWay(item, 'top', dans[i]); break
                case '2': getWay(item, 'bottom', dans[i]); break
                default : getWay(item, 'right', dans[i]); break
            }
        }
        this.dom.appendChild(docFragment)
    }
}

export default player => new Danmu(player)
